package com.example.spider.proxy.core.tester;

import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.example.spider.proxy.core.ProxyInfo;
import lombok.Data;

import java.util.Map;

import static com.example.spider.proxy.core.tester.TestConstant.DEFAULT_TEST_SUCCESS_CODE;

/**
 * 测匿名度
 */
public class UpdateAnonymityTestCallBack extends AbstractTestCallBack {

    private static final String split = "=-=-=-=-=-=-=-=-=-=-=";

    private static final String[] sourceIpHeaders = {
            "ACCPROXYWS", "Cdn-Src-Ip", "Client-IP", "client_ip",
            "CUDA_CLIIP", "Forwarded", "Forwarded-For", "REMOTE-HOST", "X-Client-Ip", "X-Coming-From", "X-Forwarded",
            "X-Forwarded-For", "X-Forwarded-For-IP", "X-Forwarded-Host", "X-Forwarded-Server", "X-Host",
            "X-Network-Info", "X-Nokia-RemoteSocket", "X-ProxyUser-IP", "X-QIHOO-IP", "X-Real-IP",
            "XCnool_forwarded_for", "XCnool_remote_addr"
    };

    private static final String[] specialProxyMarkHeaders = {
            "Mt-Proxy-ID", "Proxy-agent", "Proxy-Connection", "Surrogate-Capability", "Via", "X-Accept-Encoding",
            "X-ARR-LOG-ID", "X-Authenticated-User", "X-BlueCoat-Via", "X-Cache", "X-CID-HASH", "X-Content-Opt",
            "X-D-Forwarder", "X-Fikker", "X-Forwarded-Port", "X-Forwarded-Proto", "X-IMForwards", "X-Loop-Control",
            "X-MATO-PARAM", "X-NAI-ID", "X-Nokia-Gateway-Id", "X-Nokia-LocalSocket", "X-Original-URL", "X-Proxy-ID",
            "X-Roaming", "x-teamsite-preremap", "X-Tinyproxy", "X-TurboPage", "X-Varnish", "X-Via", "X-WAP-Profile",
            "X-WrProxy-ID", "X-XFF-0", "Xroxy-Connection"
    };

    public UpdateAnonymityTestCallBack() {
        super("http://mock.itlym.cn/proxy_info");
    }

    @Override
    public boolean onResponse(ProxyInfo proxyInfo, int responseCode, String responseStr, long costTime) {
        // 成功 or 失败

        // 有的代理会返回奇怪的报文，甚至空的；301的/301；注入广告的；返回恶意网址的；404的；Blocage；Moved Permanently；恶意改重定向地址的；只支持https的（302到https）;返回登录页面的；访问拒绝页面的 这部分直接拉黑
        // todo 判断返回内容是否符合预期

        // 计算匿名度
        String anonymity = calculateAnonymity(proxyInfo, responseStr);
        proxyInfo.setAnonymity(anonymity);
        // 分析地理位置 fixme bug
        String location = calculateLocation(proxyInfo, responseStr);
        proxyInfo.setLocation(location);
        // 计算本次请求得分
        proxyInfo.success(costTime);
        return responseCode == DEFAULT_TEST_SUCCESS_CODE;
    }

    private String calculateLocation(ProxyInfo proxyInfo, String responseStr) {
        String country = ReUtil.findAllGroup1("geoip_country_name:(\\w*?)", responseStr).get(0);
        String province = ReUtil.findAllGroup1("geoip_province_name:(\\w*?)", responseStr).get(0);
        String city = ReUtil.findAllGroup1("geoip_city_name:(\\w*?)", responseStr).get(0);
        return country + " " + province + " " + city;
    }

    /**
     * 计算匿名程度
     *
     * @param proxyInfo   代理信息
     * @param responseStr 响应信息
     * @return Transparent Anonymous Distorting Elite
     */
    private String calculateAnonymity(ProxyInfo proxyInfo, String responseStr) {
        if (responseStr.contains(LOCAL_PUBLIC_IP)) {
            // 透明：暴露自己ip，建议不要用
            return "Transparent";
        } else if (responseStr.contains(proxyInfo.getIp())) {
            // 普通匿名：虽然未暴露请求原始ip，但会暴露代理服务器ip，代理服务器比较容易被封/针对
            return "Anonymous";
        } else if (hasAnyRevealHeader(responseStr)) {
            // 高匿名：既不会暴露本机ip、也不会暴露代理服务器真实ip（每次请求随机 ip）高匿名，但能发现是代理服务器
            return "Distorting";
        } else {
            // 超高匿名：无法发现是代理服务器的高匿名代理
            return "Elite";
        }
    }

    private boolean hasAnyRevealHeader(String responseStr) {
        return StrUtil.containsAnyIgnoreCase(responseStr, sourceIpHeaders) ||
               StrUtil.containsAnyIgnoreCase(responseStr, specialProxyMarkHeaders);
    }

    @Data
    public static class HttpbinResponse {
        public Map<String, String> args;
        public Map<String, String> headers;
        public String              origin;
        public String              url;
    }
}